import { INestApplication } from "@nestjs/common";
import { APP_FILTER, APP_GUARD } from "@nestjs/core";
import { Test, TestingModule } from "@nestjs/testing";
import { JwtAuthGuard } from "../../src/utils/guards/jwt-auth.guard";
import { RolesGuard } from "../../src/utils/guards/roles.guard";
import { AuthController } from "../../src/modules/auth/auth.controller";
import * as request from "supertest";
import { User } from "../../src/modules/user/user.entity";
import { UserService } from "../../src/modules/user/user.service";
import { CreateUserDto } from "../../src/modules/user/dto/create-user.dto";
import { CompanyService } from "../../src/modules/company/company.service";
import { Company } from "../../src/modules/company/company.entity";
import { NotFoundFilter } from "../../src/utils/filter/not-found.filter";
import { DuplicateFilter } from "../../src/utils/filter/duplicate.filter";
import { CompanyGuard } from "../../src/utils/guards/company.guard";
import defaultModule from "../../src/utils/test/module.config";
import { AuthModule } from "../../src/modules/auth/auth.module";

let authController: AuthController;
let usersService: UserService;
let companyService: CompanyService;
const loginedAdmin: CreateUserDto = {
  username: "admin",
  password: "password",
  role: "admin",
};
let modRef: TestingModule;
let app: INestApplication;

let managerAToken: string;
let managerBToken: string;
let adminToken: string;

let admin: User;

let companyA: Company;
let companyB: Company;
let managerForCompanyA: User;
let managerForCompanyB: User;

describe("准备端对端测试", () => {
  beforeAll(async () => {
    modRef = await Test.createTestingModule({
      imports: [...defaultModule, AuthModule],
      providers: [
        {
          provide: APP_GUARD,
          useClass: JwtAuthGuard,
        },
        {
          provide: APP_GUARD,
          useClass: RolesGuard,
        },
        {
          provide: APP_GUARD,
          useClass: CompanyGuard,
        },
        {
          provide: APP_FILTER,
          useClass: NotFoundFilter,
        },
        {
          provide: APP_FILTER,
          useClass: DuplicateFilter,
        },
      ],
    }).compile();
    app = modRef.createNestApplication();
    await app.init();
  });
  it("运行前定义", async () => {
    authController = modRef.get<AuthController>(AuthController);
    usersService = modRef.get<UserService>(UserService);
    companyService = modRef.get<CompanyService>(CompanyService);
    expect(authController).toBeDefined();
    expect(usersService).toBeDefined();
    expect(companyService).toBeDefined();
    const users = await usersService.findAll();
    await Promise.all(users.map((item) => usersService.remove(item.id)));
    // 情况共所有公司
    const companies = await companyService.findAll();
    await Promise.all(companies.map((item) => companyService.remove(item.id)));
    // 创建系统唯一管理员
    admin = await usersService.create(loginedAdmin);
  });
  describe("普通用户", () => {
    it("JWT登录测试", async () => {
      const loginReq = await request(app.getHttpServer())
        .post("/auth/login")
        .send({
          username: loginedAdmin.username,
          password: loginedAdmin.password,
        })
        .expect(201);

      adminToken = loginReq.body.access_token;
      return request(app.getHttpServer())
        .get("/user/profile")
        .set("Authorization", "Bearer " + adminToken)
        .expect(200);
    });
  });

  describe("管理员创建公司及管理员", () => {
    // 创建公司
    it("创建公司A", async () => {
      const res = await request(app.getHttpServer())
        .post("/company")
        .send({ name: "companyA" })
        .set("Authorization", "Bearer " + adminToken)
        .expect(201);
      companyA = res.body;
    });
    it("创建公司B", async () => {
      const res = await request(app.getHttpServer())
        .post("/company")
        .send({ name: "companyB" })
        .set("Authorization", "Bearer " + adminToken)
        .expect(201);
      companyB = res.body;
    });
    it("创建公司A经理", async () => {
      const res = await request(app.getHttpServer())
        .post("/company/append/" + companyA.id)
        .send({
          username: "managerA",
          password: "password",
          name: "managerAForCompanyA",
        })
        .set("Authorization", "Bearer " + adminToken)
        .expect(201);
      managerForCompanyA = (res.body as Company).users.pop();
    });
    it("创建公司B经理", async () => {
      const res = await request(app.getHttpServer())
        .post("/company/append/" + companyB.id)
        .send({
          username: "managerB",
          password: "password",
          name: "managerBForCompanyB",
        })
        .set("Authorization", "Bearer " + adminToken)
        .expect(201);
      managerForCompanyB = (res.body as Company).users.pop();
    });

    // 创建公司管理员
  });
});
describe("登录 vs 未登录", () => {
  it("已登录", async () => {
    await request(app.getHttpServer())
      .get("/user/profile")
      .set("Authorization", "Bearer " + adminToken)
      .expect(200);
    await request(app.getHttpServer())
      .get("/user/" + admin.id)
      .set("Authorization", "Bearer " + adminToken)
      .expect(200);
    await request(app.getHttpServer())
      .get("/user/list")
      .set("Authorization", "Bearer " + adminToken)
      .expect(200);
  });
  it("未登录", async () => {
    await request(app.getHttpServer()).get("/user/profile").expect(401);
    await request(app.getHttpServer())
      .get("/user/" + admin.id)
      .expect(401);
    await request(app.getHttpServer()).get("/user/list").expect(401);
  });
});
describe("经理登录", () => {
  it("经理A登录", async () => {
    const loginReq = await request(app.getHttpServer())
      .post("/auth/login")
      .send({
        username: managerForCompanyA.username,
        password: "password",
      })
      .expect(201);

    managerAToken = loginReq.body.access_token;
  });
  it("经理B登录", async () => {
    const loginReq = await request(app.getHttpServer())
      .post("/auth/login")
      .send({
        username: managerForCompanyB.username,
        password: "password",
      })
      .expect(201);

    managerBToken = loginReq.body.access_token;
  });
});
describe("管理员 vs 经理", () => {
  it("管理员通过公司ID为公司创建经理", async () => {
    await request(app.getHttpServer())
      .post("/company/append/" + companyA.id)
      .send({
        username: "managerT",
        password: "password",
      })
      .set("Authorization", "Bearer " + adminToken)
      .expect(201);
  });
  it("经理通过公司ID为公司创建经理", async () => {
    await request(app.getHttpServer())
      .post("/company/append/" + companyA.id)
      .send({
        username: "managerT1",
        password: "password",
      })
      .set("Authorization", "Bearer " + managerAToken)
      .expect(403);
  });
  it("经理为自己的公司创建用户", async () => {
    await request(app.getHttpServer())
      .post("/company/append")
      .send({
        username: "user",
        password: "password",
      })
      .set("Authorization", "Bearer " + managerAToken)
      .expect(201);
  });
});
describe("检查自己的公司的数据", () => {
  it("A公司应当有3个用户", async () => {
    const users: (number | User[])[] = (
      await request(app.getHttpServer())
        .get("/user/list")
        .set("Authorization", "Bearer " + managerAToken)
        .expect(200)
    ).body;
    expect(users[1]).toBe(3);
  });
  it("B公司应当有1个用户", async () => {
    const users: (number | User[])[] = (
      await request(app.getHttpServer())
        .get("/user/list")
        .set("Authorization", "Bearer " + managerBToken)
        .expect(200)
    ).body;
    expect(users[1]).toBe(1);
  });
});
describe("A公司访问B公司的数据", () => {
  let userForA: User;
  let userForB: User;
  beforeAll(async () => {
    userForA = await usersService.create(
      {
        username: "userForA",
        password: "password",
      },
      companyA
    );
    userForB = await usersService.create(
      {
        username: "userForB",
        password: "password",
      },
      companyB
    );
  });

  it("A公司 访问 A公司的数据", async () => {
    await request(app.getHttpServer())
      .get("/user/" + userForA.id)
      .set("Authorization", "Bearer " + managerAToken)
      .expect(200);
  });
  it("A公司 访问 B公司的数据", async () => {
    await request(app.getHttpServer())
      .get("/user/" + userForB.id)
      .set("Authorization", "Bearer " + managerAToken)
      .expect(403);
  });
  it("B公司 访问 B公司的数据", async () => {
    await request(app.getHttpServer())
      .get("/user/" + userForB.id)
      .set("Authorization", "Bearer " + managerBToken)
      .expect(200);
  });
});
describe("经理 vs 用户", () => {
  let userToken: string;
  beforeAll(async () => {
    const res = await request(app.getHttpServer())
      .post("/auth/login")
      .send({
        username: "user",
        password: "password",
      })
      .expect(201);
    userToken = res.body.access_token;
  });
  it("用户执行不属于自己权限的操作", async () => {
    await request(app.getHttpServer())
      .post("/company/append")
      .send({
        username: "userX",
        password: "password",
      })
      .set("Authorization", "Bearer " + userToken)
      .expect(403);
  });
  it("经理执行自己权限才有的操作", async () => {
    await request(app.getHttpServer())
      .post("/company/append")
      .send({
        username: "userY",
        password: "password",
      })
      .set("Authorization", "Bearer " + managerAToken)
      .expect(201);
  });
});

afterAll(async () => {
  // 清空所有用户
  const users = await usersService.findAll();
  await Promise.all(users.map((item) => usersService.remove(item.id)));
  // 情况共所有公司
  const companies = await companyService.findAll();
  await Promise.all(companies.map((item) => companyService.remove(item.id)));
  await modRef.close();
});
